#include "OneButton.h"
#include <Arduino.h>
#include <DNSServer.h>
#include <ESPmDNS.h>  //用于设备域名 MDNS.begin("esp32")
#include <HardwareSerial.h>
#include <Preferences.h>
#include <String.h>
#include <WebServer.h>
#include <cmath>
#include <esp_wifi.h>  //用于esp_wifi_restore() 删除保存的wifi信息
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include "WiFiUdp.h"
#include <Update.h>
#include "soc/rtc_wdt.h"
// ------------------------------------------------------------------------------------------
#define CMD_BAT_IS_START "bat_is_start?"
#define CMD_BAT_IS_START_REPLY "bat_start_ok"
#define CMD_BAT_IS_START_OFF "bat_start_off"

#define CMD_BAT_STOP "bat_stop"
#define CMD_BAT_STOP_COMFIRM "bat_stop_ok"

#define CMD_BAT_START_CHARGE "bat_start_charge"
#define CMD_BAT_START_CHARGE_COMFIRM "bat_start_charge_ok"

#define CMD_BAT_CHARGE_FINISHED "bat_charge_finished"

#define CMD_BAT_FLY "bat_start_fly"
#define CMD_BAT_FLY_COMFIRM "bat_start_fly_ok"

#define PIN_BAT_EN 14  // input charge status, L开机，H未开机
#define PIN_SW_EN 13   // SW_EN 电池按键
#define PIN_SW1 27     //DJ-POW
#define PIN_SW2 25     //V+PEN


typedef enum {
  idle = 0,
  charging
} status_e;

status_e status = idle;

//sw2 V+PEN  PIN 25  打开充电=1  关闭=0  默认都是0
//sw1 DJ-POW PIN 27  打开无人机电源=0  关闭=1  默认都是0

#define SW1_ON digitalWrite(PIN_SW1, 0)
#define SW1_OFF digitalWrite(PIN_SW1, 1)

#define SW2_ON digitalWrite(PIN_SW2, 1)
#define SW2_OFF digitalWrite(PIN_SW2, 0)

#define SW_EN_H digitalWrite(PIN_SW_EN, 1)
#define SW_EN_L digitalWrite(PIN_SW_EN, 0)

#define LED_PIN 2
#define LED_ON digitalWrite(LED_PIN, 1)
#define LED_OFF digitalWrite(LED_PIN, 0)
#define LED_TOGGLE digitalWrite(LED_PIN, !digitalRead(LED_PIN))

void init_IO() {
  pinMode(LED_PIN, OUTPUT);
  pinMode(PIN_SW_EN, OUTPUT);
  pinMode(PIN_SW1, OUTPUT);
  pinMode(PIN_SW2, OUTPUT);
  pinMode(PIN_BAT_EN, INPUT);
  SW1_ON;
  SW2_OFF;
  LED_OFF;
  delay(100);
}

void blinkLED(int led, int n, int t) {
  for (int i = 0; i < 2 * n; i++) {
    LED_TOGGLE;
    delay(t);
  }
}



// ------------------------------------------------------------------------------------------
String wifi_ssid = "tptest";    //暂时存储wifi账号密码
String wifi_pass = "12345678";  //暂时存储wifi账号密码

String indexhtml = String("") + "<!DOCTYPE html>\n" + "<head>\n" + "    <meta charset=\"UTF-8\">\n" + "    <title>Update dev</title>\n" + "</head>\n" + "<body>\n" + "    <form method=\'POST\' action=\'/update\' enctype=\'multipart/form-data\'>\n" + "        <input type=\'file\' name=\'firmware\'>\n" + "        <input type=\'submit\'>\n" + "    </form>\n" + "</body>\n";
bool shouldreboot = false;  // 重启标志，固件升级操作完成后设置该标志准备重启设备

WebServer server(80);  //开启web服务, 创建TCP SERVER,参数: 端口号,最大连接数
WiFiUDP udp;
unsigned int remoteUdpPort = 9120;  // 发送远端端口
unsigned int localUdpPort = 9110;   // 监听本地端口

#define UDP_SERVER_ADDR "192.168.1.110"
IPAddress local_IP(192, 168, 1, 184);
IPAddress gateway(192, 168, 1, 1);
IPAddress subnet(255, 255, 255, 0);

uint32_t lastSendTickSec = 0;
uint32_t sendTickSec = 0;
volatile bool esp32wifiClosed = true;
// ------------------------------------------------------------------------------------------
void initWiFi() {
  WiFi.disconnect(true);
  WiFi.mode(WIFI_STA);
  WiFi.setSleep(true);
}

void wifiConnecting() {
  WiFi.setAutoConnect(true);  // 设置自动连接
  WiFi.begin(wifi_ssid.c_str(), wifi_pass.c_str());
  int connectTime = 0;
  while (!WiFi.isConnected()) {  //等待WIFI连接成功, 用于连接计时，如果长时间连接不成功，复位设备
    LED_TOGGLE;
    vTaskDelay(pdMS_TO_TICKS(50));
    if (connectTime++ > 5 * 20) {
      ESP.restart();
    }
  }
  Serial.println("WiFi connect success");
  Serial.print("local IP:");
  Serial.println(WiFi.localIP());
  debugSend("WiFi connect success");
}
void wifiTryConn() {
  if (!WiFi.isConnected()) {
    initWiFi();
    wifiConnecting();
  }
}
void handleResponse_ota()  //回调函数
{
  server.sendHeader("Connection", "close");
  server.send(200, "text/html", (Update.hasError()) ? "FAIL" : "OK");
}
void handleRoot_ota()  //回调函数
{
  String username = "flybat";
  String userpassword = "123456";
  if (!server.authenticate(username.c_str(), userpassword.c_str()))  //校验用户是否登录
  {
    return server.requestAuthentication();  //请求进行用户登录认证
  }
  server.sendHeader("Connection", "close");
  server.send(200, "text/html", indexhtml);  //登录成功则显示真正的内容
}
void handleFileupload_ota()  //回调函数
{
  HTTPUpload& upload = server.upload();    // 文件上传对象
  if (upload.status == UPLOAD_FILE_START)  // 文件上传开始
  {
    Serial.printf("start upload: %s\n", upload.filename.c_str());
    if (!Update.begin())  // 开始固件升级，检查空可用间大小，如果正在升级或空间不够则返回false
    {
      Update.printError(Serial);
    }
  } else if (upload.status == UPLOAD_FILE_WRITE)  // 文件读写中
  {
    if (Update.write(upload.buf, upload.currentSize) != upload.currentSize)  // 将文件数据写入到OTA区域
    {
      Update.printError(Serial);
    }
  } else if (upload.status == UPLOAD_FILE_END)  // 文件上传完成
  {
    Serial.println("write" + String(upload.totalSize) + "byte to OTA area");
    if (!Update.end(true))  // 完成数据写入，设置在系统重启后自动将OTA区域固件移动到Sketch区域 // Update.begin不指定大小时这里设置true
    {
      Update.printError(Serial);
    }
    Serial.println("finished, reboot and update after 1s");
    shouldreboot = true;
  } else {
    Serial.printf("firmware upload error: status=%d\n", upload.status);
  }
}


void udpSend(String msg) {
  wifiTryConn();
  udp.beginPacket(UDP_SERVER_ADDR, remoteUdpPort);
  udp.print(msg);
  udp.endPacket();
}
// ------------------------------------------------------------------------------------------
TaskHandle_t xTaskWebServer;
TaskHandle_t xTaskWdg;
TaskHandle_t xTaskStatus;
void setup() {
  Serial.begin(115200);
  Serial.println("\r\n\r\n\r\n SerialRunning setup");

  init_IO();

  if (!WiFi.config(local_IP, gateway, subnet)) {  //静态IP
    Serial.println("STA Failed to configure");
  };
  String hostName = "flybat";
  if (!WiFi.setHostname(hostName.c_str())) {
    Serial.println("Hostname failed to configure");
  }
  if (!MDNS.begin(hostName.c_str())) {  // http://flybat.local
    Serial.println("Error setting up MDNS responder!");
    while (1) {
      delay(1000);
    }
  }

  wifiTryConn();
  server.on("/", handleRoot_ota);                                             //注册链接和回调函数
  server.on("/update", HTTP_POST, handleResponse_ota, handleFileupload_ota);  // 绑定回调函数
  server.begin();                                                             //启动服务器

  udp.begin(localUdpPort);  //启动UDP监听电池UDP端口

  xTaskCreate(webServerTask, "webServerTask", 10000, NULL, 6, &xTaskWebServer);
  xTaskCreate(wdgTask, "wdgTask", 10000, NULL, 3, &xTaskWdg);
  xTaskCreate(statusTask, "statusTask", 10000, NULL, 4, &xTaskStatus);

  LED_OFF;
  Serial.println("setup done");

  debugSend("setup done");
  debugSend("version:1");

  rtc_wdt_protect_off();
  rtc_wdt_enable();
  rtc_wdt_set_time(RTC_WDT_STAGE0, 8000);
}

void debugSend(String msg) {
  wifiTryConn();
  udp.beginPacket("192.168.1.100", 10001);
  udp.print("[bat debug]: " + msg + "\n");
  udp.endPacket();
}

void wdgTask(void* params) {
  while (1) {
    LED_TOGGLE;
    delay(500);
    rtc_wdt_feed();
    wifiTryConn();
  }
}


volatile int isCharging = 0;


void statusTask(void* params) {
  while (1) {
    if (isCharging) {
      delay(15000);
      if (1 == digitalRead(PIN_BAT_EN)) {
        SW2_OFF;//停止充电
        debugSend(CMD_BAT_CHARGE_FINISHED);
        udpSend(CMD_BAT_CHARGE_FINISHED);
        isCharging = 0;
      }
    }

    delay(100);
  }
}

void webServerTask(void* params) {
  Serial.println("webServerTask running");
  while (true) {
    server.handleClient();  //处理来自客户端的请求
    if (shouldreboot) {
      delay(1000);
      ESP.restart();  // 重启设备
    }
    delay(1);
  }
}





int Data_length;
char udpIncomingPacket[255];
volatile int isPowerOn = 0;
String msg = "";
void loop() {
  Data_length = udp.parsePacket();
  if (Data_length) {
    int len = udp.read(udpIncomingPacket, 255);
    if (len > 0) {
      udpIncomingPacket[len] = 0;
    }
    msg = "";
    msg = String(udpIncomingPacket);
    debugSend(msg);

    //起飞开机
    if (msg.indexOf(CMD_BAT_FLY) != -1) {
      debugSend("bat fly cmd valid");
      SW2_OFF;
      delay(1000);      
      SW1_ON;
      delay(6000);  //延时6s
      if (isPowerOn == 0) {
        debugSend("ready to power on");
        SW_EN_H;
        delay(200);
        SW_EN_L;
        delay(1000);
        SW_EN_H;
        delay(2500);
        SW_EN_L;
        debugSend(CMD_BAT_FLY_COMFIRM);
        udpSend(CMD_BAT_FLY_COMFIRM);
        isPowerOn = 1;
      } else {
        debugSend("already power on");
        debugSend(CMD_BAT_FLY_COMFIRM);
        udpSend(CMD_BAT_FLY_COMFIRM);
      }
    }

    //关机
    if (msg.indexOf(CMD_BAT_STOP) != -1) {
      debugSend("power off cmd valid");
      if (isPowerOn) {
        debugSend("power on, ready to off");
        SW_EN_H;
        delay(200);
        SW_EN_L;
        delay(1000);
        SW_EN_H;
        delay(2500);
        SW_EN_L;
        isPowerOn = 0;
      } else {
        debugSend("already power off");
      }
      debugSend(CMD_BAT_STOP_COMFIRM);
      udpSend(CMD_BAT_STOP_COMFIRM);
    }

    //充电
    if (msg.indexOf(CMD_BAT_START_CHARGE) != -1) {
      debugSend("start charge cmd valid");
      SW1_OFF;
      delay(1000);   
      SW2_ON;
      isCharging = 1;
      isPowerOn = 0;
      debugSend(CMD_BAT_START_CHARGE_COMFIRM);
      udpSend(CMD_BAT_START_CHARGE_COMFIRM);
    }

    //底座询问电池是否开机
    if (msg.indexOf(CMD_BAT_IS_START) != -1) {
      debugSend("bat is start? cmd valid");
      if (isPowerOn) {
        debugSend(CMD_BAT_IS_START_REPLY);
        udpSend(CMD_BAT_IS_START_REPLY);
      } else {
        debugSend(CMD_BAT_IS_START_OFF);
        udpSend(CMD_BAT_IS_START_OFF);
      }

      delay(100);
    }
  }
  delay(10);
}